<?php

namespace Biz\Services\User;

use Lumen\Logger\Logger;
use Biz\Models\User\User;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Biz\Libraries\NicknameUtil;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Hash;
use Lumen\Security\PasswordSecurity;
use Biz\Constants\User\UserConstants;
use Biz\Criteria\User\EmailCriterion;
use Biz\Criteria\User\MobileCriterion;
use Biz\Criteria\User\UsernameCriterion;
use Biz\Repositories\User\UserRepository;
use Lumen\Core\Exceptions\BusinessException;
use Biz\Criteria\User\EmailOrMobileCriterion;
use Biz\Services\Passport\AccessTokenService;
use Laravel\Passport\PersonalAccessTokenResult;

/**
 * UserService.
 *
 * @license [http://www.85do.com] [杭州永奥网络科技有限公司]
 * @copyright Copyright (c) 2018-2026 Hangzhou Yongao Technology Co., Ltd. All rights reserved.
 */
class UserService
{
    /**
     * @var \Lumen\Repository\Contracts\RepositoryContract
     */
    protected $repository;

    /**
     * @var \Lumen\Repository\Contracts\RepositoryContract
     */
    protected $accessTokenService;

    /**
     * RepositoryController constructor.
     *
     * @param UserRepository     $repository
     * @param AccessTokenService $accessTokenService
     */
    public function __construct(UserRepository $repository, AccessTokenService $accessTokenService)
    {
        $this->repository         = $repository;
        $this->accessTokenService = $accessTokenService;
    }

    /**
     * Store a new client.
     *
     * @param \Illuminate\Http\Request $request
     *
     * @return User
     */
    public function store(Request $request): User
    {
        $data = $request->all();

        return $this->create($data);
    }

    /**
     * 创建新用户.
     *
     * @param array $data
     *
     * @throws \Exception
     * @throws BusinessException
     *
     * @return User
     */
    public function create(array $data): User
    {
        $allowField = $this->repository->createModel()->getFillable();
        $data       = array_only($data, $allowField);

        // Check Email
        if (isset($data['email'])) {
            $email = $data['email'];
            if ($this->findByEmail($email)) {
                throw new BusinessException(trans('user.email_exist'));
            }
        }

        // Check Mobile
        if (isset($data['mobile'])) {
            $mobile = $data['mobile'];
            if ($this->findByMobile($mobile)) {
                throw new BusinessException(trans('user.mobile_exist'));
            }
        }

        // Handle type
        if (! isset($data['type'])) {
            $data['type'] = UserConstants::TYPE_USER;
        }
        // Handle avatar
        if (! isset($data['avatar'])) {
            $data['avatar'] = UserConstants::DEFAULT_AVATAR;
        }
        // Handle nickname
        if (! isset($data['nickname'])) {
            $data['nickname'] = NicknameUtil::randomNickname();
        }
        // Handle password
        if (isset($data['password'])) {
            $data['salt']     = Str::random(6);
            $data['password'] = PasswordSecurity::hash($data['salt'], $data['password']);
        }

        // 1) Start Transaction
        DB::beginTransaction();
        try {
            // 创建用户
            /** @var User $user */
            $user = $this->repository->create($data);
            if (! $user) {
                throw new BusinessException(trans('user.create_fail'));
            }

            // 创建accessToken
            /** @var PersonalAccessTokenResult $personalAccessTokenResult */
            $personalAccessTokenResult = $this->accessTokenService->getPersonalAccessToken($user);
            $user->setPersonalAccessTokenResult($personalAccessTokenResult);

            // 2) Commit Transaction
            DB::commit();
        } catch (\Exception $e) {
            // 3) Rollback Transaction
            DB::rollback();
            // 4) Log Transaction
            Logger::error(__CLASS__, __FUNCTION__, $e);
            // 5) Throw Exception
            throw $e;
        }

        return $user;
    }

    /**
     * 登录.
     *
     * @param array $data
     *
     * @throws \Exception
     * @throws BusinessException
     *
     * @return User
     */
    public function login(array $data): User
    {
        $username = $data['username'];
        $password = $data['password'];

        /** @var User $user */
        $user = $this->repository
            ->setCacheLifetime(0)
            ->pushCriterion(new EmailOrMobileCriterion($username))
            ->findFirst();

        if (is_null($user)) {
            throw new BusinessException(trans('user.user_does_no_exist'));
        }

        // 检测密码
        if (! PasswordSecurity::check($user->salt, $password, $user->password)) {
            throw new BusinessException(trans('user.incorrect_password'));
        }

        try {
            // 更新密码
            if (PasswordSecurity::needsRehash($user->password)) {
                $password = PasswordSecurity::hash($user->salt, $password);
                $this->repository->update($user->id, [
                    'password' => $password,
                ]);
            }

            // 创建accessToken
            /** @var PersonalAccessTokenResult $personalAccessTokenResult */
            $personalAccessTokenResult = $this->accessTokenService->getPersonalAccessToken($user);
            $user->setPersonalAccessTokenResult($personalAccessTokenResult);
        } catch (\Exception $e) {
            // Log Transaction
            Logger::error(__CLASS__, __FUNCTION__, $e);
            // Throw Exception
            throw $e;
        }

        return $user;
    }

    /**
     * @param $id
     *
     * @throws BusinessException
     *
     * @return \Biz\Models\User\User
     */
    public function find($id)
    {
        $user = $this->repository->find($id);
        if (is_null($user)) {
            throw new BusinessException(trans('user.user_not_found'));
        }

        return $user;
    }

    /**
     * 使用Email查找用户.
     *
     * @param $email
     *
     * @return User | null
     */
    public function findByEmail($email)
    {
        return $this->repository
            ->setCacheLifetime(0)
            ->pushCriterion(new EmailCriterion($email))
            ->findFirst();
    }

    /**
     * 使用Mobile查找用户.
     *
     * @param $mobile
     *
     * @return User | null
     */
    public function findByMobile($mobile)
    {
        return $this->repository
            ->setCacheLifetime(0)
            ->pushCriterion(new MobileCriterion($mobile))
            ->findFirst();
    }

    /**
     * 使用Username查找用户.
     *
     * @param $username
     *
     * @return User | null
     */
    public function findByUsername($username)
    {
        return $this->repository
            ->setCacheLifetime(0)
            ->pushCriterion(new UsernameCriterion($username))
            ->findFirst();
    }

    /**
     * 重置密码
     *
     * @param User $user
     * @param string $password
     * @return User
     */
    public function resetPassword(User $user, string $password): User
    {
        $user->password = PasswordSecurity::hash($user->salt, $password);
        $user->update();

        return $user;
    }
}
